# -*- coding: utf-8 -*-
"""
流水线步骤基类
定义所有步骤的通用接口和行为
"""

from abc import ABC, abstractmethod
from enum import Enum
from typing import Any, Dict, Optional, Callable
from pathlib import Path
from datetime import datetime
import json
import logging


logger = logging.getLogger(__name__)


class StepStatus(Enum):
    """步骤状态枚举"""
    PENDING = "pending"  # 待执行
    RUNNING = "running"  # 执行中
    COMPLETED = "completed"  # 已完成
    FAILED = "failed"  # 执行失败
    SKIPPED = "skipped"  # 已跳过
    CANCELLED = "cancelled"  # 已取消


class StepResult:
    """步骤执行结果"""

    def __init__(
        self,
        success: bool,
        message: str = "",
        data: Optional[Dict[str, Any]] = None,
        output_files: Optional[list[Path]] = None,
    ):
        self.success = success
        self.message = message
        self.data = data or {}
        self.output_files = output_files or []
        self.timestamp = datetime.now()

    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "success": self.success,
            "message": self.message,
            "data": self.data,
            "output_files": [str(f) for f in self.output_files],
            "timestamp": self.timestamp.isoformat(),
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "StepResult":
        """从字典创建"""
        result = cls(
            success=data["success"],
            message=data.get("message", ""),
            data=data.get("data", {}),
            output_files=[Path(f) for f in data.get("output_files", [])],
        )
        if "timestamp" in data:
            result.timestamp = datetime.fromisoformat(data["timestamp"])
        return result


class BaseStep(ABC):
    """流水线步骤基类"""

    # 步骤配置
    name: str  # 步骤名称
    description: str  # 步骤描述
    required_inputs: list[str]  # 必需输入
    optional_inputs: list[str]  # 可选输入
    produces_outputs: list[str]  # 产生输出

    # 步骤属性
    output_dir: Path  # 输出目录
    force_run: bool = False  # 强制执行
    skip_on_success: bool = True  # 成功后跳过

    def __init__(
        self,
        output_dir: Path,
        force_run: bool = False,
        skip_on_success: bool = True,
    ):
        """
        初始化步骤

        Args:
            output_dir: 输出目录
            force_run: 是否强制执行
            skip_on_success: 是否在成功后跳过
        """
        self.output_dir = Path(output_dir)
        self.force_run = force_run
        self.skip_on_success = skip_on_success

        self.status = StepStatus.PENDING
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None
        self.error_message: str = ""
        self.progress_callback: Optional[Callable[[int, str], None]] = None

        # 创建输出目录
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def report_progress(self, percent: int, message: str = ""):
        """报告进度"""
        if self.progress_callback:
            self.progress_callback(percent, message)
        logger.info(f"[{self.name}] {percent}% - {message}")

    @abstractmethod
    def validate_inputs(self) -> tuple[bool, list[str]]:
        """
        验证输入参数

        Returns:
            (是否有效, 错误列表)
        """
        pass

    @abstractmethod
    def run(self) -> StepResult:
        """
        执行步骤

        Returns:
            执行结果
        """
        pass

    def can_skip(self) -> bool:
        """
        检查是否可以跳过

        Returns:
            是否可以跳过
        """
        if self.force_run:
            return False

        if not self.skip_on_success:
            return False

        return self.status == StepStatus.COMPLETED

    def execute(self) -> StepResult:
        """
        执行步骤（包含生命周期管理）

        Returns:
            执行结果
        """
        # 检查是否可以跳过
        if self.can_skip():
            self.status = StepStatus.SKIPPED
            self.end_time = datetime.now()
            logger.info(f"[{self.name}] Skipped (already completed)")
            return StepResult(
                success=True,
                message="Skipped (already completed)",
                data={"skipped": True},
            )

        # 验证输入
        is_valid, errors = self.validate_inputs()
        if not is_valid:
            self.status = StepStatus.FAILED
            self.error_message = "; ".join(errors)
            logger.error(f"[{self.name}] Validation failed: {self.error_message}")
            return StepResult(
                success=False,
                message=f"Validation failed: {self.error_message}",
                data={"errors": errors},
            )

        # 执行步骤
        try:
            self.status = StepStatus.RUNNING
            self.start_time = datetime.now()
            logger.info(f"[{self.name}] Starting execution")

            result = self.run()

            # 处理结果
            if result.success:
                self.status = StepStatus.COMPLETED
                self.end_time = datetime.now()
                logger.info(f"[{self.name}] Completed successfully")
            else:
                self.status = StepStatus.FAILED
                self.error_message = result.message
                self.end_time = datetime.now()
                logger.error(f"[{self.name}] Failed: {result.message}")

            return result

        except Exception as e:
            self.status = StepStatus.FAILED
            self.error_message = str(e)
            self.end_time = datetime.now()
            logger.exception(f"[{self.name}] Exception occurred")
            return StepResult(
                success=False,
                message=f"Exception: {str(e)}",
                data={"exception": str(e)},
            )

    def get_duration(self) -> float:
        """
        获取执行时长（秒）

        Returns:
            时长
        """
        if not self.start_time:
            return 0.0

        end_time = self.end_time or datetime.now()
        return (end_time - self.start_time).total_seconds()

    def get_status_dict(self) -> Dict[str, Any]:
        """
        获取状态字典

        Returns:
            状态字典
        """
        return {
            "name": self.name,
            "description": self.description,
            "status": self.status.value,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "duration": self.get_duration(),
            "error_message": self.error_message,
        }

    def __str__(self) -> str:
        return f"{self.name} ({self.status.value})"

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(name='{self.name}')"
